﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using LightSO.ILibrary;
using LightSO.ILibrary.Mapping;

namespace LightSO.Library
{
    public class RepositoryFactory : ILibraryFactory
    {
        static readonly object lockObj = new object();
        static Dictionary<Type, dynamic> dict = new Dictionary<Type, dynamic>();

        public T Create<T>() where T : InterfaceBase
        {
            var generic = typeof(T).GetGenericArguments();

            var id = string.Format("Repository_{0}", typeof(T).GetHashCode());

            var context = new Spring.Context.Support.StaticApplicationContext();

            var _att = Attribute.GetCustomAttribute(generic[0], typeof(DBDataSource));
            if (((DBDataSource)_att).ProviderType == ProviderType.DapperQ)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.DapperQ.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.MongoDb)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.MongoDb.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.Redis)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.Redis.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.EF)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.EF.Repository`1").MakeGenericType(generic), null);
            }

            return (T)context.GetObject(id);
        }

        public T Single<T>() where T : InterfaceBase
        {
            var generic = typeof(T).GetGenericArguments();

            var id = string.Format("Repository_{0}", typeof(T).GetHashCode());

            var context = new Spring.Context.Support.StaticApplicationContext();

            var _att = Attribute.GetCustomAttribute(generic[0], typeof(DBDataSource));
            if (((DBDataSource)_att).ProviderType == ProviderType.DapperQ)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.DapperQ.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.MongoDb)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.MongoDb.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.Redis)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.Redis.Repository`1").MakeGenericType(generic), null);
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.EF)
            {
                context.RegisterPrototype(id, Type.GetType("LightSO.Library.EF.Repository`1").MakeGenericType(generic), null);
            }

            return (T)context.GetObject(id);
        }

        public T1 Create<T1, T2>() 
            where T1 : class, InterfaceBase, IRepository<T2> 
            where T2 : EntityBase, new()
        {
            var t = typeof(T1);
            var _att = Attribute.GetCustomAttribute(typeof(T2), typeof(DBDataSource));
            if (((DBDataSource)_att).ProviderType == ProviderType.DapperQ)
            {
                return new DapperQ.Repository<T2>() as T1;
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.MongoDb)
            {
                return new MongoDb.Repository<T2>() as T1;
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.Redis)
            {
                return new Redis.Repository<T2>() as T1;
            }
            else if (((DBDataSource)_att).ProviderType == ProviderType.EF)
            {
                return new Redis.Repository<T2>() as T1;
            }
            else
            { 
                return default(T1);
            }
        }

        public T1 Single<T1, T2>() 
            where T1 : class, InterfaceBase, IRepository<T2> 
            where T2 : EntityBase, new()
        {
            var t = typeof(T1);
            if (!dict.ContainsKey(t))
            {
                lock (lockObj)
                {
                    if (!dict.ContainsKey(t))
                    {
                        var _att = Attribute.GetCustomAttribute(typeof(T2), typeof(DBDataSource));
                        if (((DBDataSource)_att).ProviderType == ProviderType.DapperQ)
                        {
                            return new DapperQ.Repository<T2>() as T1;
                        }
                        else if (((DBDataSource)_att).ProviderType == ProviderType.MongoDb)
                        {
                            return new MongoDb.Repository<T2>() as T1;
                        }
                        else if (((DBDataSource)_att).ProviderType == ProviderType.Redis)
                        {
                            return new Redis.Repository<T2>() as T1;
                        }
                        else if (((DBDataSource)_att).ProviderType == ProviderType.EF)
                        {
                            return new EF.Repository<T2>(((EFDataSource)_att).DbContext) as T1;
                        }
                        else
                        {
                            return default(T1);
                        }
                    }
                }
            }

            return dict[t] as T1;
        }
    }
}
